#pragma once
#ifndef _KH_ENGINE_RESOURCE_H
#define _KH_ENGINE_RESOURCE_H
#include <Kharlia/Engine/Common.h>

#include <CEGUISize.h>
#include <CEGUITexture.h>

#include <hash_map>
#include <set>

namespace kharlia { namespace engine {

class FResource;
class CClient;

struct _PyStrHash: public stdext::hash_compare<str> {
    size_t operator()(const str& s) const {
        return static_cast<size_t>(PyObject_Hash(s.ptr()));
    }
    bool operator()(const str& s1, const str& s2) const {
        return static_cast<size_t>(PyObject_Hash(s1.ptr())) <
               static_cast<size_t>(PyObject_Hash(s2.ptr()));
    }
};

class FResourceManager: public enable_shared_from_this<FResourceManager>,
                        public noncopyable {
    typedef stdext::hash_map<str, shared_ptr<FResource>, _PyStrHash> ResourcesType;
    typedef std::set<str> GroupsType;
    ResourcesType mResources;
    GroupsType mGroups;


public:
    FResourceManager() {}

    void UnloadResources();
    void UnloadResources(str groupName);
    void UnregisterResources();
    void RegisterResource(shared_ptr<FResource> res);
    void UnregisterResource(shared_ptr<FResource> res);
    shared_ptr<FResource> GetResource(str name);

    friend void _InitResourceClasses();
    friend void _InitResourceBindings();
};

enum EResourceState {
    RS_UNLOADED,
    RS_LOADING,
    RS_LOADED,
    RS_UNLOADING
};

class FResource: public boost::enable_shared_from_this<FResource>,
                 public noncopyable {
protected:
    weak_ptr<FResourceManager> mManager;
    str mName;
    str mGroup;
    EResourceState mState;

public:
    FResource(str name, str group);
    virtual ~FResource();

    virtual void Unload() = 0;

    inline shared_ptr<FResourceManager> GetManager() const { return mManager.lock(); }
    inline str GetName() const { return mName; }
    inline str GetGroup() const { return mGroup; }
    inline EResourceState GetState() const { return mState; }
    inline bool GetIsLoaded() const { return GetState() == RS_LOADED; }
    
    friend void _InitResourceClasses();
    friend void _InitResourceBindings();
    friend void FResourceManager::RegisterResource(shared_ptr<FResource>);
    friend void FResourceManager::UnregisterResource(shared_ptr<FResource>);
};

class FTexture: public FResource {
    ui::Texture* mIntTexture; // Internal CEGUI Texture
    shared_ptr<CClient> mClient;

public:
    FTexture(str name, str group, ui::Texture* tex=NULL,
             shared_ptr<CClient> client=shared_ptr<CClient>());
    virtual ~FTexture();

    virtual void Unload();
    void DestroyIntTexture();

    inline CEGUI::Texture* GetIntTexture() const { return mIntTexture; }
    inline void SetIntTexture(CEGUI::Texture* tex) { DestroyIntTexture(); mIntTexture = tex; }

    inline bool GetIsValid() const { return GetIntTexture() != NULL; }
    inline uint16 GetWidth() const { return uint16(mIntTexture? mIntTexture->getOriginalDataSize().d_width: 0); }
    inline uint16 GetHeight() const { return uint16(mIntTexture? mIntTexture->getOriginalDataSize().d_height: 0); }

    friend void _InitResourceClasses();
    friend void _InitResourceBindings();
};

inline shared_ptr<FResource> FResourceManager::GetResource(str name) {
    ResourcesType::iterator it = mResources.find(name);
    return it == mResources.end()? shared_ptr<FResource>(): it->second;
}

inline void FTexture::Unload() {
    DestroyIntTexture();
}

}}

#endif